#ifndef COROUTINE_TASK_H
#define COROUTINE_TASK_H

#include <cstdint>
#include <memory>
#include <chrono>
#include <libucontext/bits.h>
#include <libucontext/libucontext.h>

class Task {
public:
    virtual ~Task() = default;
    virtual void period_task() { };


    inline uint32_t get_period() const {
        return period_ms;
    }

    inline int64_t get_next_run_time() const {
        return next_run_time;
    }

    inline bool get_finished() const {
        return finished;
    }

    inline void cancle_period_task() {
        period_ms = UINT32_MAX;
    }

    static inline auto get_timestamp_ms() {
        return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
    }

    inline void set_exit() {
        next_run_time = get_timestamp_ms();
        running = false;
    }

    const libucontext_ucontext_t* get_entry_context() {
        return &entry_context;
    }

    void task_init(libucontext_ucontext_t* context) {
        stack = std::make_unique<uint8_t[]>(stack_size);    //申请内存作为栈
        libucontext_getcontext(&entry_context);             //makecontext需要通过getcontext初始化一个ucontext
        entry_context.uc_stack.ss_sp = stack.get();         //传递栈指针
        entry_context.uc_stack.ss_size = stack_size;        //传递栈大小
        entry_context.uc_link = context;                    //当新context运行完后的返回context，这里返回调度器的context
        libucontext_makecontext(&entry_context, (void (*)()) task_helper, 1, this); //制造一个指向task_helper的context，并传递本类指针
        return_context = context;                           //设置返回context
        next_run_time = get_timestamp_ms();                 //设置next_run_time为当前时间戳，即期待马上切换该context
    }

protected:
    static void task_helper(Task* t) {
        while(t->running) {
            auto time_stamp = get_timestamp_ms();
            if (t->period_ms != UINT32_MAX) {
                t->period_task();
            }
            t->next_run_time = time_stamp + t->period_ms;
            t->raw_task_yield();
        }
        t->next_run_time = INT64_MAX;
        t->finished = true;
        t->raw_task_yield();
    }

    void task_yield() {
        next_run_time = get_timestamp_ms() + period_ms;
        libucontext_swapcontext(&entry_context, return_context);
    }

    void task_yield(int64_t sleep_time_ms) {
        next_run_time = get_timestamp_ms() + sleep_time_ms;
        libucontext_swapcontext(&entry_context, return_context);
    }

    void raw_task_yield() {
        libucontext_swapcontext(&entry_context, return_context);
    }

protected:
    uint32_t period_ms{UINT32_MAX};
    libucontext_ucontext_t entry_context{};
    libucontext_ucontext_t* return_context{nullptr};
    std::unique_ptr<uint8_t[]> stack{nullptr};
    const uint32_t stack_size{64*1024};
    int64_t next_run_time{0};
    bool running{true};
    bool finished{false};
};

#endif //COROUTINE_TASK_H
